package jespeak.rules;

import jespeak.dict.Dictionary;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Rule implements Comparable<Rule> {

	private String pre;
	private String match;
	private String post;
	private String phoneme;
	private Dictionary dict;
	private int voiceNumber;
	private boolean conditional;
	private boolean hasConditional;
	private final static Logger logger = Logger.getLogger(Rule.class.getName());

	private int matchPre(String prePart) {
		logger.log(Level.INFO, "matchPre called for {0} and {1}", new Object[]{pre, prePart});
		if (prePart != null) {
			int i = prePart.lastIndexOf(" ");
			if (i != -1) {
				prePart = prePart.substring(i+1);
				if (prePart.length() == 0)
					prePart = null;
			}
		}
		logger.log(Level.INFO, "matchPre after check {0} and {1}", new Object[]{pre, prePart});
		if (pre.equals("_") && prePart == null)
			return 1;
		if (prePart == null)
			return -1;
		if (pre.equals("A") && dict.isVowel(prePart.charAt(prePart.length()-1)))
			return 40;
		if (pre.equals("&") && prePart != null) {
			// needs changing
			return 19;
		}
		if (pre.equals(prePart))
			return 20 + pre.length();
		if (pre.length() < prePart.length()) {
			if (pre.equals(prePart.substring(0, pre.length())))
				return 20 + pre.length();
		}
		return -1;
	}

	private int matchPost(String postPart) {
//		System.out.println("post previous :" + postPart + ":");
		if (postPart == null) {
			if (post.equals("_"))
				return 1;
			else
				return -1;
		}
		int i = postPart.indexOf(" ");
		if (i == 0)
			postPart = null;
		else if(i != -1)
			postPart = postPart.substring(0, i);
		if (postPart == null) {
			if (post.equals("_"))
				return 1;
			else
				return -1;
		}
//		if (post.equals("p"))
//			System.out.println("post :" + postPart + ":");
		if (post.equals("C") && dict.isConsonant(postPart.charAt(0)))
			return 19;
		if (post.equals("A") && dict.isVowel(postPart.charAt(0)))
			return 40;
		if (postPart.length() >= 2)
			if (post.equals("CA") && dict.isConsonant(postPart.charAt(0)) && dict.isVowel(postPart.charAt(1)))
				return 32;
		if (post.equals(postPart))
			return 20 + post.length();
		if (post.endsWith("_")) {
			String t = post.substring(0, post.length());
			if (t.equals(postPart))
				return 90;
		}
		logger.log(Level.INFO, "matchPost checks for {0} and {1}", new Object[]{post, postPart});
		if (post.length() < postPart.length()) {
			String mpart = postPart.substring(0, post.length());
			logger.log(Level.INFO, "matchPost checks for {0} and :{1}:", new Object[]{post, mpart});
			if (post.equals(mpart))
				return 20 + post.length();
		}
		return -1;
	}

	public MatchedRule match(String word, int position) {
		word = word.toLowerCase();
		int matchLength = match.length(), score = 0;
		String matchPart = null;
		try {
			matchPart = word.substring(position, position + matchLength);
		}
		catch (IndexOutOfBoundsException e) {
			return null;
		}
		if (matchPart.equals(match) == false)
			return null;
		score += matchLength;
		logger.log(Level.INFO, "Rule: {0}", this);
		logger.log(Level.INFO, "Matched {0} and {1}", new Object[]{match, matchPart});
		// match is the same -- check pre and post
		if (pre != null) {
			String prePart = null;
			logger.log(Level.INFO, "PreTrying {0} and {1}", new Object[]{pre, prePart});
			try {
				prePart = word.substring(0, position);
				if (prePart.length() == 0)
					prePart = null;
			}
			catch (IndexOutOfBoundsException e) {
				prePart = null;
			}
			logger.log(Level.INFO, "Will call matchPre :{0}: and :{1}:", new Object[]{pre, prePart});
			int rc = matchPre(prePart);
			if (rc == -1)
				return null;
			else
				score += rc;
		}
		logger.log(Level.INFO, "Pre-Matched OK");

		// if here then pre is matched
		if (post != null) {
			String postPart = null;
			logger.log(Level.INFO, "PostTrying {0} and {1} for word {2}", new Object[]{post, postPart, word});
			try {
				postPart = word.substring(position + matchLength);
				if (postPart.length() == 0)
					postPart = null;
			}
			catch (IndexOutOfBoundsException e) {
				postPart = null;
			}
			logger.log(Level.INFO, "Will call matchPost :{0}: and :{1}:", new Object[]{post, postPart});
			int rc = matchPost(postPart);
			if (rc == -1)
				return null;
			else
				score += rc;
		}
		logger.log(Level.INFO, "Post-Matched OK returning {0}", score);
		// if here then rule matches word
		MatchedRule mr = new MatchedRule(this, score, matchLength);
		return mr;
	}

	public Rule(String line, Dictionary dict) {
		this.dict = dict;
		hasConditional = false;
		pre = null;
		post = null;
		match = null;
		phoneme = null;
		line = line.trim();
		StringTokenizer st = new StringTokenizer(line, " ");
		String currentToken = st.nextToken();
		if (currentToken.startsWith("?!")) {
			hasConditional = true;
			conditional = false;
			voiceNumber = Integer.parseInt(currentToken.substring(2));
			currentToken = st.nextToken();
		} else if (currentToken.startsWith("?")) {
			hasConditional = true;
			conditional = true;
			voiceNumber = Integer.parseInt(currentToken.substring(1));
			currentToken = st.nextToken();
		}
		int pos = currentToken.indexOf(")");
		if (pos != -1) {
			pre = currentToken.substring(0, pos);
			currentToken = st.nextToken();
		}
		match = currentToken;
		if (st.hasMoreTokens() == false)
			return;
		currentToken = st.nextToken();
		pos = currentToken.indexOf("(");
		if (pos != -1) {
			post = currentToken.substring(pos + 1);
			if (st.hasMoreTokens())
				currentToken = st.nextToken();
			else
				currentToken = null;
		}
		phoneme = currentToken;
	}

	public String getPhoneme() {
		return phoneme;
	}

	@Override
	public String toString() {
		String s = "";
		if (hasConditional && conditional)
				s = "?" + voiceNumber;
		else if (hasConditional && !conditional)
				s = "?!" + voiceNumber;
		s += "\t";
		if (pre != null)
			s += pre + ")";
		s += "\t" + match + "\t";
		if (post != null)
			s += "(" + post;
		if (phoneme != null)
			s += "\t[" + phoneme + "]";
		else
			s += "\t[]";
		return s;
	}

	public int compareTo(Rule o) {
		return match.compareTo(o.match);
	}
}
